/**
 * Copyright 2009 University of  Washington
 * This code is released under the GPL version 3 - with a copy of the license included
 * @author Maya Hao Li
 *
 */
package seedpod.model.rdb;

import java.io.PrintWriter;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import seedpod.model.RdbCls;
import seedpod.model.SeedpodModel;
import seedpod.rdb.RdbSql;
import seedpod.rdb.SqlUtil;
import edu.stanford.smi.protege.model.BrowserSlotPattern;
import edu.stanford.smi.protege.model.Cls;
import edu.stanford.smi.protege.model.Model;


public class MetaRdbCls {
	public static String BROWSER_PATTERN = "browserPattern";
	public static String CLS_ID = "cid";
	public static String DOCUMENTATION = "documentation";
	public static String FRAME_ID = "frameID";
	public static String INLINE = "inline";
	public static String IS_CONCRETE = "isConcrete";
	public static String JAVA_CLASS = "javaClass";
	public static String METACLSNAME = SeedpodModel.DEFAULT_META_RDB_CLASS;
	public static String NAME = "name";
	public static String PARENT = "parent";
	public static final String PRIMARY_KEY = "primaryKey";
	public static String TABLE_NAME = "tableName";
	public static String CLS_TYPE = "clsType";
	public static String USER_DEFINED_NAME = "userDefinedName";
	public static String VIEW_NAME = "viewName";

	private static Relation _tblRdbCls;
	public static MetaRdbCls create( RdbCls rdbCls, Relation relInstance, View view ) {
		MetaRdbCls instance = new MetaRdbCls();
		instance.init( rdbCls, relInstance, view );
		return instance;
	}
	public static MetaRdbCls create( ResultSet rs ) throws SQLException {
		MetaRdbCls instance = new MetaRdbCls();
		instance.instance( rs );
		return instance;
	}

	public static Relation getSchema() {
		return _tblRdbCls;
	}

	public static void serializeSchema( PrintWriter out ) {
		out.println( RdbSql.createTable( _tblRdbCls ) );
	}

	private static boolean _isTableInitialized = false;

	private HashMap<String, Object> _metadataInstance = new HashMap<String, Object>();

	public MetaRdbCls() {
		if ( !_isTableInitialized ) {
			_isTableInitialized = true;
			new MetaRdbClsRelation();
		}
	}

	public String getBrowserPattern() {
		return (String)_metadataInstance.get( BROWSER_PATTERN );
	}

	public HashMap<String, Object> getClsInstanceData() {
		return _metadataInstance;
	}

	public String getClsType() {
		return (String)_metadataInstance.get( CLS_TYPE );
	}

	public String getDocumentation() {
		return (String)_metadataInstance.get( DOCUMENTATION );
	}

	public Integer getFrameId() {
		return (Integer)_metadataInstance.get( FRAME_ID );
	}

	public String getName() {
		String name = (String)_metadataInstance.get( NAME );
		return name;
	}


	public String getParentName() {
		String p = (String)_metadataInstance.get( PARENT );
		return (p == null) ? "" : p;
	}

	public String getPrimaryKey() {
		String pk = (String)_metadataInstance.get( PRIMARY_KEY );
		return (pk == null) ? "" : pk;
	}

	public String getTableName() {
		String t = (String)_metadataInstance.get( TABLE_NAME );
		return (t == null) ? "" : t;
	}

	public String getUserDefinedName() {
		return (String)_metadataInstance.get( USER_DEFINED_NAME );
	}

	public String getViewName() {
		String v = (String)_metadataInstance.get( VIEW_NAME );
		return (v == null) ? "" : v;
	}

	public Boolean isConcrete() {
		return (Boolean)_metadataInstance.get( IS_CONCRETE );
	}

	public Boolean isInline() {
		return (Boolean)_metadataInstance.get( INLINE );
	}

	public void serializeInstance( PrintWriter out ) {
		out.println( genInsertStatementl( _tblRdbCls, getClsInstanceData() ) );
	}
	
	
	/**
	 * TODO injection risk in inserting string values w/ single quote. consider using getInsertPreparedStatement
	 * instead.*
	 */
	public static String genInsertStatementl( Relation rel, HashMap<String, Object> data ) {
		String insertStmt = "INSERT INTO "  + SqlUtil.SP + SqlUtil.QQ( rel.getName() ) + SqlUtil.NL + SqlUtil.TAB;
		String keyString = "", valString = "";
		Vector<Attribute> attributes = rel.getAttributes(); // data.keySet().iterator();
		for ( int i = 0; i < attributes.size(); i++ ) {
			Attribute att = (Attribute)attributes.elementAt( i );

			Object val = data.get( att.getName() );
			keyString += SqlUtil.QQ( att.getName() );
			try {
				valString += RdbSql.toSqlValString( val, att ); 
			}
			catch (Exception exc) {
				System.err.println( exc.getMessage() );
				System.err.println( "errr in RdbSql.insertRowSql " + rel.getName() + " " + att.getName()
						+ " is null.  The errorneous SQL is commented out with -- in the output files. " );

				return " -- Insert statement terminated:  " + valString;
			}

			keyString += (i < attributes.size() - 1) ? SqlUtil.C : "";
			valString += (i < attributes.size() - 1) ? SqlUtil.C : "";
		}
		insertStmt += SqlUtil.PARAN( keyString ) + SqlUtil.NL + " VALUES " + SqlUtil.PARAN( valString ) + SqlUtil.SC;
		return insertStmt;
	}

	@SuppressWarnings("unchecked")
	public void setBrowserPattern( BrowserSlotPattern browserKey ) {
		String browserKeyString = "";
		if ( browserKey != null ) {
			List key = browserKey.getElements();
			for ( int i = 0; i < key.size(); i++ )
				browserKeyString += key.get( i );
		} else {
			browserKeyString = getName() + " VAL(id)";
		}

		setBrowserPattern( browserKeyString );
	}

	public void setBrowserPattern( String pattern ) {
		_metadataInstance.put( BROWSER_PATTERN, pattern );
	}
	
	public Integer getCid() {
		return (Integer)_metadataInstance.get( CLS_ID );
	}
	
	public void setCid( Integer id ) {
		_metadataInstance.put( CLS_ID, id ); 
	}

	public void setClsType( String metaCls ) {
		_metadataInstance.put( CLS_TYPE, metaCls );
	}

	@SuppressWarnings("unchecked")
	public void setDocumentation( Collection documentation ) {
		String doc = "";
		Iterator cIter = documentation.iterator();
		if ( cIter.hasNext() )
			doc = (String)cIter.next();
		_metadataInstance.put( DOCUMENTATION, doc );
	}

	public void setDocumentation( String doc ) {
		_metadataInstance.put( DOCUMENTATION, doc );
	}

	public void setFrameId( int frame_id ) {
		_metadataInstance.put( FRAME_ID, new Integer( frame_id ) );
	}

	public void setInline( Boolean isInline ) {
		_metadataInstance.put( INLINE, isInline );
	}

	public void setIsConcrete( boolean isConcrete ) {
		_metadataInstance.put( IS_CONCRETE, new Boolean( isConcrete ) );
	}

	public void setName( String _name ) {
		_metadataInstance.put( NAME, _name );
	}

	public void setParentName( Cls parentCls ) {
		if ( parentCls != null )
			setParentName( parentCls.getName() );
		else
			setParentName( "" );
	}

	public void setParentName( String parent ) {
		_metadataInstance.put( PARENT, parent );
	}

	public void setPrimaryKey( String pk ) {
		_metadataInstance.put( PRIMARY_KEY, pk );
	}

	public void setTableName( String name ) {
		_metadataInstance.put( TABLE_NAME, name );
	}

	public void setUserDefinedName( String userName ) {
		_metadataInstance.put( USER_DEFINED_NAME, userName );
	}

	public void setViewName( String name ) {
		_metadataInstance.put( VIEW_NAME, name );
	}

	private void setPrimaryKey( Vector<Attribute> primaryKeySet ) {
		String s = "";
		for ( int i = 0; i < primaryKeySet.size(); i++ ) {
			if ( i > 0 )
				s += MetaRdbSlot.VALUE_DELIMITER;
			s += primaryKeySet.elementAt( i ).getName();
		}
		setPrimaryKey( s );
	}

	protected void init( RdbCls rdbCls, Relation relation, View view ) {
		Cls cls = rdbCls.getCls();
		setFrameId( cls.getFrameID().getLocalPart() );
		setName( cls.getName() );
		setUserDefinedName( rdbCls.getUserDefinedName() );
		setClsType( rdbCls.getClsMetaCls().getName() );
		setParentName( rdbCls.getParentCls() );
		setIsConcrete( cls.isConcrete() );
		setDocumentation( cls.getDocumentation() );
		setInline( rdbCls.isInline() );
		setBrowserPattern( cls.getDirectBrowserSlotPattern() );

		if ( relation != null ) {
			setTableName( relation.getName() );
			setPrimaryKey( relation.getPrimaryKeySet() );
		}
		if ( view != null )
			setViewName( view.getName() );
	}

	protected void instance( ResultSet rs ) throws SQLException {
		setFrameId( rs.getInt( FRAME_ID ) );
		setName( rs.getString( NAME ) );
		setUserDefinedName( rs.getString( USER_DEFINED_NAME ) );
		setClsType( rs.getString( CLS_TYPE ) );
		setParentName( rs.getString( PARENT ) );
		setPrimaryKey( rs.getString( PRIMARY_KEY ) );
		setIsConcrete( rs.getBoolean( IS_CONCRETE ) );
		setDocumentation( rs.getString( DOCUMENTATION ) );
		setInline( rs.getBoolean( INLINE ) );
		setBrowserPattern( rs.getString( BROWSER_PATTERN ) );
		setTableName( rs.getString( TABLE_NAME ) );
		setViewName( rs.getString( VIEW_NAME ) );
	}

	/**
	 * Schema for the :RDB_CLASS
	 */
	class MetaRdbClsRelation {
		public MetaRdbClsRelation() {
			_tblRdbCls = new Relation( SeedpodModel.DEFAULT_META_RDB_CLASS );
			createRdbClsTable();
		}

		public Relation getRelation() {
			return _tblRdbCls;
		}

		private void createRdbClsTable() {
			Attribute att_cid = new Attribute( _tblRdbCls, CLS_ID, RdbValueType.SERIAL, true, null );
			Attribute att_frameID = new Attribute( _tblRdbCls, FRAME_ID, RdbValueType.INTEGER, true, null );
			Attribute att_name = new Attribute( _tblRdbCls, NAME, RdbValueType.VARCHAR, true, null );
			att_name.setDatabaseParameter( 64 );
			att_name.setIsUnique( true );

			Attribute att_userName = new Attribute( _tblRdbCls, USER_DEFINED_NAME, RdbValueType.TEXT, false, null );

			Attribute att_type = new Attribute( _tblRdbCls, CLS_TYPE, RdbValueType.VARCHAR, true,
					SeedpodModel.DEFAULT_META_RDB_CLASS );
			att_type.setDatabaseParameter( 64 );

			Attribute att_parent = new Attribute( _tblRdbCls, PARENT, RdbValueType.VARCHAR, false, Model.Cls.THING );
			att_parent.setDatabaseParameter( 64 );

			Attribute att_primaryKey = new Attribute( _tblRdbCls, PRIMARY_KEY, RdbValueType.VARCHAR, false,
					Model.Cls.THING );
			att_primaryKey.setDatabaseParameter( 200 );

			Attribute att_inline = new Attribute( _tblRdbCls, INLINE, RdbValueType.BOOLEAN, true, "false" );
			Attribute att_isconcrete = new Attribute( _tblRdbCls, IS_CONCRETE, RdbValueType.BOOLEAN, true, "true" );
			Attribute att_documentation = new Attribute( _tblRdbCls, DOCUMENTATION, RdbValueType.TEXT, false, "" );
			Attribute att_browser = new Attribute( _tblRdbCls, BROWSER_PATTERN, RdbValueType.VARCHAR, true,
					"VAL(name) VAL(id)" );
			att_browser.setDatabaseParameter( 100 );

			Attribute att_tblName = new Attribute( _tblRdbCls, TABLE_NAME, RdbValueType.VARCHAR, false, "" );
			att_tblName.setDatabaseParameter( 64 );

			Attribute att_viewName = new Attribute( _tblRdbCls, VIEW_NAME, RdbValueType.VARCHAR, false, "" );
			att_viewName.setDatabaseParameter( 64 );

			Attribute att_javaClass = new Attribute( _tblRdbCls, JAVA_CLASS, RdbValueType.VARCHAR, false, "" );
			att_javaClass.setDatabaseParameter( 200 );

			_tblRdbCls.setPrimaryKey( att_cid );
			_tblRdbCls.addAttribute( att_cid );
			_tblRdbCls.addAttribute( att_frameID );
			_tblRdbCls.addAttribute( att_name );
			_tblRdbCls.addAttribute( att_userName );
			_tblRdbCls.addAttribute( att_type );
			_tblRdbCls.addAttribute( att_parent );
			_tblRdbCls.addAttribute( att_primaryKey );
			_tblRdbCls.addAttribute( att_inline );
			_tblRdbCls.addAttribute( att_isconcrete );
			_tblRdbCls.addAttribute( att_documentation );
			_tblRdbCls.addAttribute( att_browser );
			_tblRdbCls.addAttribute( att_tblName );
			_tblRdbCls.addAttribute( att_viewName );
			_tblRdbCls.addAttribute( att_javaClass );
		}
	}


}
